<template>
  <div class="upload-file">
    <el-upload
      multiple
      :action="uploadFileUrl"
      :before-upload="handleBeforeUpload"
      :file-list="fileList"
      :limit="limit"
      :on-error="handleUploadError"
      :on-exceed="handleExceed"
      :on-success="handleUploadSuccess"
      :show-file-list="false"
      :headers="headers"
      class="upload-file-uploader"
      ref="fileUpload"
    >
      <!-- 上传按钮 -->
      <el-button size="mini" type="primary" v-if="!disabled"
        >选取文件</el-button
      >
      <!-- 上传提示 -->
      <div class="el-upload__tip" slot="tip" v-if="showTip">
        请上传
        <template v-if="fileSize">
          大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
        </template>
        <template v-if="fileType">
          格式为 <b style="color: #f56c6c">{{ fileType.join('/') }}</b>
        </template>
        的文件
      </div>
    </el-upload>
    <!-- 文件列表 -->
    <transition-group
      class="upload-file-list el-upload-list el-upload-list--text"
      name="el-fade-in-linear"
      tag="ul"
    >
      <li
        :key="file.url + '' + index"
        class="el-upload-list__item ele-upload-list__item-content"
        v-for="(file, index) in fileList"
      >
        <el-link :href="file.url" :underline="false" target="_blank">
          <span class=""> {{ getFileName(file.name) }} </span>
        </el-link>
        <div style="margin-right: 10px; flex: none" v-if="!disabled">
          <el-link :underline="false" @click="handleDelete(index)" type="danger"
            >删除</el-link
          >
        </div>
      </li>
    </transition-group>

    <!-- 上传任务列表 -->
    <transition-group
      class="upload-file-list el-upload-list el-upload-list--text"
      name="el-fade-in-linear"
      tag="ul"
    >
      <li
        :key="index"
        :keys="1"
        class="el-upload-list__item ele-upload-list__item-content"
        v-for="(file, index) in uploadTasks"
      >
        <span class="el-icon-document"> {{ file.obj.name }} </span>
        <!-- 显示进度条 -->
        <el-progress
          v-if="file.obj.progress !== undefined"
          :percentage="file.obj.progress"
          style="margin-top: 20px"
        ></el-progress>

        <!-- 暂停和继续上传按钮 -->
        <div class="ele-upload-list__item-content-action">
          <el-button
            size="mini"
            type="warning"
            @click="handlePauseUpload(file.file)"
            v-if="!file.isPaused"
            >暂停上传</el-button
          >
          <el-button
            size="mini"
            type="success"
            @click="handleResumeUpload(file.file)"
            v-if="file.isPaused"
            >继续上传</el-button
          >
          <el-link
            :underline="false"
            @click="handleDelete1(index)"
            type="danger"
            >删除</el-link
          >
        </div>
      </li>
    </transition-group>
  </div>
</template>

<script>
import { getToken } from '@/utils/auth';
import SparkMD5 from 'spark-md5';
// import Vue from "vue"; // 确保引入 Vue

import {
  fileChunkUploadChunk,
  selectChunkNumberByMd5,
} from '@/api/file/fragmentation';

export default {
  name: 'FileUpload',
  props: {
    // 值
    value: [String, Object, Array],
    disabled: {
      type: Boolean,
      default: false,
    },
    // 数量限制
    limit: {
      type: Number,
      default: 5,
    },
    // 大小限制(MB)
    fileSize: {
      type: Number,
      default: 5,
    },
    // 文件类型, 例如['png', 'jpg', 'jpeg']
    fileType: {
      type: Array,
      default: () => ['doc', 'xls', 'ppt', 'txt', 'pdf'],
    },
    // 是否显示提示
    isShowTip: {
      type: Boolean,
      default: true,
    },
  },
  data() {
    return {
      number: 0,
      uploadTasks: [], // 上传任务列表
      uploadList: [],
      uploadList1: [],
      uploadList2: [],
      fileList: [],

      uploadFileUrl: process.env.VUE_APP_BASE_API + '/file/upload', // 上传文件服务器地址
      headers: {
        Authorization: 'Bearer ' + getToken(),
      },
      fileList: [],
      chunkSize: 1024 * 1024, // 每个分片的大小，1MB
      isUploading: false, // 是否正在上传
      isPaused: false, // 是否暂停上传
      currentFile: null, // 当前正在上传的文件
      currentChunk: 0, // 当前正在上传的分片编号
      chunks: 0, // 总分片数
      fileMd5: '', // 文件的 MD5 值
      fileProgress: {}, // 用于存储每个文件的上传进度
      fileProgress1: {}, // 用于存储每个文件的上传进度
    };
  },
  watch: {
    value: {
      handler(val) {
        if (val) {
          let temp = 1;
          const list = Array.isArray(val) ? val : this.value.split(',');
          this.fileList = list.map((item) => {
            if (typeof item === 'string') {
              item = { name: item, url: item };
            }
            item.uid = item.uid || new Date().getTime() + temp++;
            return item;
          });
          console.log('fileList after watch:', this.fileList); // 添加日志
        } else {
          this.fileList = [];
          return [];
        }
      },
      deep: true,
      immediate: true,
    },
  },
  computed: {
    // 是否显示提示
    showTip() {
      return this.isShowTip && (this.fileType || this.fileSize);
    },
    // 过滤掉已经存在于 fileList 中的 uploadTasks
    filteredUploadTasks() {
      return this.uploadTasks.filter((task) => {
        // 使用 file.md5 或 file.uid 作为唯一标识符
        const isInFileList = this.fileList.some(
          (file) => file.md5 === task.fileMd5 || file.uid === task.file.uid
        );
        return !isInFileList;
      });
    },
  },
  methods: {
    // 上传前校检格式和大小
    handleBeforeUpload(file) {
      // 校检文件类型
      file.uid = new Date().getTime() + Math.random(); // 生成唯一标识符
      if (this.fileType.length) {
        const fileName = file.name.split('.');
        const fileExt = fileName[fileName.length - 1];
        const isTypeOk = this.fileType.indexOf(fileExt) >= 0;
        if (!isTypeOk) {
          this.$modal.msgError(
            `文件格式不正确, 请上传${this.fileType.join('/')}格式文件!`
          );
          return false;
        }
      }
      // 校检文件大小
      if (this.fileSize) {
        const isLt = file.size / 1024 / 1024 < this.fileSize;
        if (!isLt) {
          this.$modal.msgError(`上传文件大小不能超过 ${this.fileSize} MB!`);
          return false;
        }
      }

      // 初始化进度条为0
      this.fileProgress[file.name] = 0;

      // 检查是否已经存在相同的任务
      const existingTask = this.uploadTasks.find((task) => task.file === file);
      if (existingTask) {
        this.$modal.msgError('文件已在上传队列中');
        return false;
      }

      this.number++;
      this.isUploading = true;
      this.currentFile = file;

      this.uploadTasks.push({
        obj: {
          name: file.name,
          progress: 0,
        },
        file,
        currentChunk: 0,
        chunks: Math.ceil(file.size / this.chunkSize),
        isPaused: false,
        isUploading: true,
        fileMd5: '',
      });

      this.uploadFileInChunks(file);
      return false; // 阻止默认上传行为
    },

    // 分片上传文件
    async uploadFileInChunks(file) {
      const task = this.uploadTasks.find((task) => task.file === file);
      const that = this;

      if (!task) return;

      const fileSize = file.size;
      task.chunks = Math.ceil(fileSize / this.chunkSize);
      task.currentChunk = 0;
      const spark = new SparkMD5.ArrayBuffer();
      const fileReader = new FileReader();

      // 初始化进度为0
      this.fileProgress[file.name] = 0;

      // 计算文件的 MD5 值
      task.fileMd5 = await this.calculateFileMd5(file);

      // 调用接口，获取已上传的分片数量
      const uploadedChunks = await this.getUploadedChunks(task.fileMd5);
      if (
        typeof uploadedChunks === 'string' &&
        (uploadedChunks.startsWith('http://') ||
          uploadedChunks.startsWith('https://'))
      ) {
        this.handleUploadSuccess(
          {
            code: 200,
            msg: uploadedChunks,
          },
          { raw: file }
        );
        return;
      }

      task.currentChunk = uploadedChunks;
      this.updateProgress(file.name, 0); // 初始化进度

      const loadNext = () => {
        const start = task.currentChunk * this.chunkSize;
        const end =
          start + this.chunkSize >= fileSize
            ? fileSize
            : start + this.chunkSize;
        fileReader.readAsArrayBuffer(file.slice(start, end));
      };

      fileReader.onload = async (e) => {
        if (task.isPaused) return; // 如果暂停，则不处理
        spark.append(e.target.result);
        loadNext(); // 继续加载下一个分片
      };

      fileReader.onerror = () => {
        this.$modal.msgError('文件读取失败');
      };

      loadNext();

      // 上传队列，一次上传 1 个分片
      const uploadQueue = [];
      const maxConcurrentUploads = 3;
      const uploadNextChunk = async () => {
        if (
          task.currentChunk < task.chunks &&
          uploadQueue.length < maxConcurrentUploads &&
          !task.isPaused
        ) {
          const chunkNumber = task.currentChunk;
          uploadQueue.push(chunkNumber);
          task.currentChunk++; // 立即更新 currentChunk

          try {
            await that.uploadChunk(
              file,
              chunkNumber,
              task.fileMd5,
              task.chunks
            );
            uploadQueue.splice(uploadQueue.indexOf(chunkNumber), 1);

            if (task.currentChunk < task.chunks) {
              uploadNextChunk();
            } else if (uploadQueue.length === 0) {
              this.$modal.msgSuccess('文件上传完成');
              task.isUploading = false; // 上传完成，更新状态
              this.uploadedSuccessfully(file);
            }
          } catch (err) {
            this.$modal.msgError(`上传失败: ${err.message}`);
          }
        }
      };

      for (
        let i = 0;
        i < maxConcurrentUploads && task.currentChunk < task.chunks;
        i++
      ) {
        uploadNextChunk();
      }
    },

    // 文件个数超出
    handleExceed() {
      this.$modal.msgError(`上传文件数量不能超过 ${this.limit} 个!`);
    },

    // 上传失败
    handleUploadError(err) {
      this.$modal.msgError('上传文件失败，请重试');
    },

    // 上传成功回调
    handleUploadSuccess(res, file) {
      if (res.code === 200) {
        if (res.msg) {
          this.uploadList.push({
            type: file.raw.type,
            size: file.raw.size,
            name: file.raw.name,
            url: res.msg,
          });
          this.uploadList1.push({
            type: file.raw.type,
            size: file.raw.size,
            name: file.raw.name,
            url: res.msg,
          });
          this.uploadedSuccessfully(file.raw);
          // 从 uploadTasks 中移除该文件的任务
          this.uploadTasks = this.uploadTasks.filter(
            (task) => task.file !== file
          );
          console.log('fileList after upload success:', this.fileList); // 添加日志
        }
      } else {
        this.number--;
        this.$modal.msgError(res.msg);
        this.$refs.fileUpload.handleRemove(file);
        this.uploadedSuccessfully(file.raw);
      }
    },
    // 删除文件
    handleDelete(index) {
      this.fileList.splice(index, 1);
      this.uploadList.splice(index, 1);
      this.uploadList1.splice(index, 1);
      this.$emit('input', this.listToString(this.fileList));
    },

    // 删除文件
    handleDelete1(index) {
      this.uploadTasks[index].isPaused = true;
      this.uploadTasks.splice(index, 1);
    },

    // 上传结束处理
    uploadedSuccessfully(file) {
      try {
        if (this.number > 0 && this.uploadTasks.length === this.number) {
          this.fileList = this.fileList.concat(this.uploadList1);
          this.fileList = this.fileList.filter(
            (item, index, self) =>
              index === self.findIndex((t) => t.uid === item.uid)
          );
          this.uploadList1 = [];
          this.number = 0;
          const index = this.uploadTasks.findIndex(
            (task) => task.file === file
          );
          if (index !== -1) {
            this.uploadTasks.splice(index, 1);
          }
          this.$emit('input', this.listToString(this.fileList));
          console.log('fileList after uploadedSuccessfully:', this.fileList); // 添加日志
        }
      } catch (Err) {
        console.log(Err);
      }
    },

    // 获取文件名称
    getFileName(name) {
      if (name.lastIndexOf('/') > -1) {
        return name.slice(name.lastIndexOf('/') + 1);
      } else {
        return name;
      }
    },

    // 对象转成指定字符串分隔
    listToString(list, separator) {
      let strs = '';
      separator = separator || ',';
      for (let i in list) {
        strs += list[i].url + separator;
      }
      return strs != '' ? strs.substr(0, strs.length - 1) : '';
    },

    // 计算文件的 MD5 值
    async calculateFileMd5(file) {
      return new Promise((resolve, reject) => {
        const spark = new SparkMD5.ArrayBuffer();
        const fileReader = new FileReader();

        fileReader.onload = (e) => {
          spark.append(e.target.result);
          resolve(spark.end());
        };

        fileReader.onerror = () => {
          reject('文件读取失败');
        };

        fileReader.readAsArrayBuffer(file);
      });
    },

    // 获取已上传的分片数量
    async getUploadedChunks(fileMd5) {
      try {
        const res = await selectChunkNumberByMd5({ md5: fileMd5 });

        if (res.code == 200) {
          if (res.hasOwnProperty('data')) {
            return res.data;
          } else {
            return res.msg;
          }
        } else {
          this.$modal.msgError(res.msg);
          return 0;
        }
      } catch (err) {
        console.log(err);
        this.$modal.msgError('获取已上传分片数量失败');
        return 0;
      }
    },

    // 上传分片
    async uploadChunk(file, chunkNumber, fileMd5, chunks) {
      const that = this;
      const start = chunkNumber * this.chunkSize;
      const end =
        start + this.chunkSize >= file.size
          ? file.size
          : start + this.chunkSize;
      const chunkFile = file.slice(start, end);
      const formData = new FormData();
      formData.append('file', chunkFile);
      formData.append('name', file.name);
      formData.append('totalChunks', Math.ceil(file.size / this.chunkSize));
      formData.append('chunkNumber', chunkNumber + 1); // 添加分片编号
      formData.append('md5', fileMd5);

      try {
        const res = await fileChunkUploadChunk(formData); // 等待分片上传

        if (res.code === 200) {
          console.log(`Chunk ${chunkNumber} uploaded successfully`);

          // 计算并更新进度
          const progress = (((chunkNumber + 1) / chunks) * 100).toFixed(0) / 1;
          this.updateProgress(file.name, progress);

          // 如果是最后一片分片上传成功，调用 handleUploadSuccess 并传递完整文件地址
          if (chunkNumber === chunks - 1) {
            this.handleUploadSuccess(
              {
                code: 200,
                msg: res.msg,
              },
              { raw: file }
            );
            const index = this.uploadTasks.findIndex(
              (task) => task.file === file
            );
            if (index !== -1) {
              this.uploadTasks.splice(index, 1);
            }
            return res; // 返回响应并结束函数
          }

          return res; // 返回响应以继续处理下一个分片
        } else {
          this.$modal.msgError(`上传失败: ${res.msg}`);
          throw new Error(`Failed to upload chunk ${chunkNumber}: ${res.msg}`);
        }
      } catch (err) {
        this.$modal.msgError(`上传失败: ${err.message}`);
        throw err; // 抛出错误以便在调用函数中捕获
      }
    },

    // 更新进度条
    updateProgress(fileName, progress) {
      this.uploadTasks.forEach((task) => {
        if (task.obj.name === fileName) {
          task.obj.progress = progress;
        }
      });
    },

    // 暂停上传
    handlePauseUpload(file) {
      const task = this.uploadTasks.find((task) => task.file === file);
      if (task) {
        task.isPaused = true;
        this.$modal.msgSuccess('上传已暂停');
        console.log('fileList after pause:', this.fileList); // 添加日志
      }
    },

    // 继续上传
    handleResumeUpload(file) {
      const task = this.uploadTasks.find((task) => task.file === file);
      if (task) {
        task.isPaused = false;
        this.$modal.msgSuccess('继续上传');
        this.uploadFileInChunks(file);
        console.log('fileList after resume:', this.fileList); // 添加日志
      }
    },
  },
};
</script>

<style scoped lang="scss">
.upload-file-uploader {
  margin-bottom: 5px;
}

.upload-file-list .el-upload-list__item {
  border: 1px solid #e4e7ed;
  line-height: 2;
  margin-bottom: 10px;
  position: relative;
}

.upload-file-list .ele-upload-list__item-content {
  display: flex;
  justify-content: space-between;
  align-items: center;
  color: inherit;
}

.ele-upload-list__item-content-action .el-link {
  margin-right: 10px;
}

.el-icon-document {
  width: 70%;
  // flex: none;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.ele-upload-list__item-content-action {
  margin-top: 5px;
  flex: none;
}

// :deep(.el-upload-list__item .el-progress__text){
//   top: 14px;
// }
.el-upload-list__item {
  margin-top: 20px;
}
</style>
